﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Pg.BioMedics.SDR.Entities;
using Pg.BioMedics.SDR.Data.Constants;

namespace Pg.BioMedics.SDR.Data
{
    /// <summary>
    /// Provides entry point for all data access layer routines 
    /// related with Document and satelite entities.
    /// </summary>
    public class DocumentManager : BaseDataManager
    {
        #region Constructors

        /// <summary>
        /// Creates instance of DocumentManager. Provides all required initializations 
        /// according to the given strategy.
        /// </summary>
        /// <param name="dataMapperStrategy">Data Mapper strategy used within current context</param>
        internal DocumentManager(IDataMapperStrategy dataMapperStrategy)
            : base(dataMapperStrategy) { }

        #endregion

        #region Methods

        /// <summary>
        /// Inserts a new document into the datbase
        /// </summary>
        /// <param name="document">New document instance</param>
        /// <returns>Saved document</returns>
        public virtual Document InsertDocument(Document document)
        {
            try
            {
                DataMapper.Insert(DocumentStoredProcedures.InsertDocument, document);
                ThrowInsertDocumentException(document);

                return document;
            }
            catch (Exception ex)
            {
                throw new SDRDataException("Cannot insert document", ex);
            }
        }     

        /// <summary>
        /// Gets document by id
        /// </summary>
        /// <param name="documentId">Document unique id</param>
        /// <returns>Document instance or null if document doesnot exist</returns>
        public virtual Document GetDocumentById(int documentId)
        {
            try
            {
                Dictionary<string, object> parameters = new Dictionary<string, object>();
                parameters.Add("DocumentId", documentId);

                return DataMapper.QueryForObject<Document>(DocumentStoredProcedures.GetDocumentById, parameters);
            }
            catch (Exception ex)
            {
                throw new SDRDataException("Cannot get document by id {0}", ex, documentId);
            }
        }

        /// <summary>
        /// Sets document status
        /// </summary>
        /// <param name="document">Existsing document</param>
        /// <param name="statusCode">New status code</param>
        /// <param name="author">Status modification author</param>
        public virtual void SetDocumentStatus(Document document, string statusCode, Person author)
        {
            try
            {
                Dictionary<string, object> parameters = new Dictionary<string, object>();
                parameters.Add("DocumentId", document.Id);
                parameters.Add("StatusCode", statusCode);
                parameters.Add("PersonId", author.Id);

                DataMapper.Insert(DocumentStoredProcedures.SetDocumentStatus, parameters);
                ThrowSetDocumentStatusException((int)parameters["Result"], document, statusCode, author);
            }
            catch (Exception ex)
            {
                throw new SDRDataException("Cannot set document status", ex);
            }
        }   

        /// <summary>
        /// Gets language codes
        /// </summary>
        /// <returns>Array with language codes</returns>
        public virtual string[] GetLanguageCodes()
        {
            try
            {
                return DataMapper.QueryForList<string>(DocumentStoredProcedures.GetLanguageCodes, null).ToArray();
            }
            catch (Exception ex)
            {
                throw new SDRDataException("Cannot get language codes", ex);
            }
        }

        /// <summary>
        /// Gets documents stored in the system by its status code and status author
        /// </summary>
        /// <param name="statusCode">Document status code</param>
        /// <param name="statusAuthor">Document status author</param>
        /// <returns>List of documents</returns>
        public virtual IList<Document> GetDocumentsByStatus(string statusCode, Person statusAuthor)
        {
            try
            {
                Dictionary<string, object> parameters = new Dictionary<string,object>();
                parameters.Add("StatusCode", statusCode);
                parameters.Add("StatusAuthorId", statusAuthor.Id);

                return DataMapper.QueryForList<Document>(DocumentStoredProcedures.GetDocumentsByStatus, parameters);
            }
            catch (Exception ex)
            {
                throw new SDRDataException("Cannot get documents by status", ex);
            }
        }

        /// <summary>
        /// Gets list of documents replaced by given document. If the document desnot replace any documents,
        /// this method will return empty list.
        /// </summary>
        /// <param name="document">Document instance</param>
        /// <returns>List of replaced documents</returns>
        public virtual IList<Document> GetReplacedDocuments(Document document)
        {
            try
            {
                Dictionary<string, object> parameters = new Dictionary<string, object>();
                parameters.Add("DocumentId", document.Id);

                return DataMapper.QueryForList<Document>(DocumentStoredProcedures.GetReplacedDocuments, parameters);
            }
            catch (Exception ex)
            {
                throw new SDRDataException("Cannot get replaced documentsby document id {0}", ex, document.Id);
            }
        }

        /// <summary>
        /// Gets list of all documents stored in the datastore
        /// </summary>
        /// <returns>List of documents</returns>
        public virtual IList<Document> GetAllDocuments()
        {
            try
            {
                return DataMapper.QueryForList<Document>(DocumentStoredProcedures.GetAllDocuments, new Dictionary<string, object>());
            }
            catch (Exception ex)
            {
                throw new SDRDataException("Could not get all documents", ex);
            }
        }

        #endregion

        #region Support methods

        /// <summary>
        /// Throws appropriate exception if InserDocument stored procedure returns
        /// error code
        /// </summary>
        /// <param name="document"></param>
        private void ThrowInsertDocumentException(Document document)
        {
            switch (document.Id)
            {
                case -1:
                    throw new SDRDataException("Document type code {0} is invalid", document.TypeCode);
                case -2:
                    throw new SDRDataException("Language code {0} is invalid", document.LanguageCode);
                case -3:
                    throw new SDRDataException("Parent document {0} doesnnot exist", document.OriginalId);
            }
        }

        /// <summary>
        /// Throws appropriate exception for error code returned by SetDaocumentStatus stored procedure
        /// </summary>
        /// <param name="result">Error code</param>
        /// <param name="document">Document instance</param>
        /// <param name="statusCode">Document status code</param>
        /// <param name="author">Document status transition author</param>
        private void ThrowSetDocumentStatusException(int result, Document document, string statusCode, Person author)
        {
            switch (result)
            {
                case -1:
                    throw new SDRDataException("Document {0} doesnot exist", document.Id);
                case -2:
                    throw new SDRDataException("Document status code {0} is invalid", statusCode);
                case -3:
                    throw new SDRDataException("Person {0} doesnot exist", author.Id);
            }
        }

        #endregion
    }
}
